#pragma once

#include "spline.h"
#include "drawing.h"
#include "font.h"

namespace Tmpl {

struct WidgetStyle
{
	Pixel cTop, cBtm, cBorder, cHighLight;
	Pixel cText, cTextDark;
};

extern WidgetStyle StyleStandard;
extern WidgetStyle StyleBrown;
extern WidgetStyle StyleWhite;

// ===========================================================
// Base Widget
// ===========================================================

class Widget
{
public:
	enum WidgetType
	{
		TEXT,
		BUTTON,
		TEXTBOX,
		SLIDER,
		CHECKBOX,
		DROPLIST,
		SELECTLIST,
		SCROLLBAR,
		TEXTLOG,
		CURVEGRAPH,
		WIDGETSET
	};
public:
	Widget(const char* label, int w, int h, int x=0, int y=0);
	virtual ~Widget() {delete myLabel;}
	virtual WidgetType GetType() const = 0;

	void SetStyle(WidgetStyle style);
	void SetLabel(const char* label);
	virtual void SetLocked(bool locked);
	virtual void SetPos(int x, int y);
	virtual void SetSize(int w, int h);

	static void SetDefaultStyle(WidgetStyle style);
	static WidgetStyle GetDefaultStyle();

	Vec2i GetPos() const;
	Vec2i GetSize() const;
	const char* GetLabel() const;
	bool IsLocked() const;
	bool IsChanged() const;

	virtual void Tick(float deltaTime) = 0;
	virtual void Draw() = 0;
protected:
	char* myLabel;
	int myX, myY, myW, myH;
	WidgetStyle myStyle;
	bool myIsLocked, myIsChanged;

	static WidgetStyle myDefaultStyle;
};

// ===========================================================
// Specialized Widgets
// ===========================================================

class StaticText : public Widget
{
public:
	StaticText(const char* label, int w, int h, int x=0, int y=0);
	WidgetType GetType() const {return TEXT;}

	void SetAttribs(Font* font, Pixel color, Font::TextAlign halign=Font::CENTER, Font::TextAlign valign=Font::CENTER);
	void Tick(float deltaTime) {}
	void Draw();
private:
	Font* mFont;
	char* mText;
	Pixel mColor;
	Font::TextAlign myHalign, myValign;
};

class Button : public Widget
{
public:
	Button(const char* label, int w, int h, int x=0, int y=0);
	WidgetType GetType() const {return BUTTON;}

	bool IsClicked() const {return myIsClicked;}
	bool IsDown() const {return myIsDown;}

	void Tick(float deltaTime);
	void Draw();
private:
	bool myIsMouseOver, myIsClicked, myIsDown;
};

class TextBox : public Widget
{
public:
	TextBox(const char* label, int w, int h, int x=0, int y=0, int maxChars=32, int textBoxH=16);
	~TextBox(){ delete myContent; }
	WidgetType GetType() const {return TEXTBOX;}

	void Tick(float deltaTime);
	void Draw();
	
	void SetContent(const char* content);
	const char* GetContent() {return myContent;}
	void SetSelected(bool selected);

private:
	void _DeleteMarked();
	void _ClipCursorPos();
	void _ChangeCursorPos(int newPos, bool resetMarker);

	int myMaxChars, myTextBoxH;
	char* myContent;
	bool myIsMouseOver, myIsSelected;
	int myCursorPos, myMarkerPos;
	float myPixelsOffset;
	float myFlickerTimer;
};

class Slider : public Widget
{
public:
	Slider(const char* label, int w, int h, int x=0, int y=0, bool compact=true, int decimals=0);
	WidgetType GetType() const {return SLIDER;}

	void Tick(float deltaTime);
	void Draw();
	
	void SetValue(double value);
	void SetRange(double min, double max);
	double GetValue() const {return myValue;}
	int GetIntValue() const {return (int)(myValue+0.45);}

private:
	int myDecimalPlaces;
	bool myIsMouseOver, myIsSelected, myIsCompact;
	double myMinValue, myMaxValue, myValue;
};

class CheckBox : public Widget
{
public:
	CheckBox(const char* label, int w, int h, int x=0, int y=0, bool checked=false, int boxSize=10);
	WidgetType GetType() const {return CHECKBOX;}

	void SetChecked(bool checked) {myIsChecked = checked;}
	bool IsChecked() const {return myIsChecked;}
	bool HasChanged() const {return myIsChanged;}

	void Tick(float deltaTime);
	void Draw();
private:
	int myBoxSize;
	bool myIsMouseOver, myIsChecked;
};

class ListItem
{
	friend class DropList;
	friend class SelectList;
public:
	ListItem(const char* label);
	virtual ~ListItem() {delete myName;}
	virtual void Draw(int w, int h, int x, int y, const WidgetStyle& style, bool selected);
protected:
	char* myName;
};

struct DropListDataImp;
class DropList : public Widget
{
public:
	DropList(const char* label, int w, int h, int x=0, int y=0, int itemyH=14);
	~DropList();
	WidgetType GetType() const {return DROPLIST;}

	void Clear();
	void AddItem(const char* name);
	void AddItemCustom(ListItem* item);
	int GetSelected() const {return myIsSelected;}
	void SetSelected(int index);
	
	void Tick(float deltaTime);
	void Draw();

private:
	DropListDataImp* myData;
	int myIsSelected, myIsMouseOverItem, myItemH;
	bool myIsMouseOver, myIsDown;
};

struct SelectListDataImp;
class SelectList : public Widget
{
public:
	SelectList(const char* label, int w, int h, int x=0, int y=0, int itemyH=12);
	~SelectList();
	WidgetType GetType() const {return SELECTLIST;}

	void Clear();
	void AddItem(const char* label);
	void AddItemCustom(ListItem* item);
	int GetSelected() const {return myIsSelected;}
	void SetSelected(int index);
	
	void Tick(float deltaTime);
	void Draw();

private:
	SelectListDataImp* myData;
	int myIsSelected, myIsMouseOverItem, myItemH;
};

class ScrollBar : public Widget
{
public:
	ScrollBar(const char* label, int w, int h, int x=0, int y=0, bool horizontal=false, bool addButtons=true);
	~ScrollBar();
	WidgetType GetType() const {return SCROLLBAR;}

	void Tick(float deltaTime);
	void Draw();

	void SetPos(int x, int y);
	void SetSize(int w, int h);
	void SetRange(int realArea, int visibleArea);

	int GetScrollPos() const {return myScrollPos;}
	bool IsFull() const	{return myRealArea <= myVisibleArea;}

private:
	Button* myScrollUpButton, *myScrollDownButton;
	int myScrollPos, myRealArea, myVisibleArea;
	bool myIsMouseOver, myIsHorizontal, myIsDragging, myHasButtons;
	int myDragPos, myDragScroll;
};

struct TextLogDataImp;
class TextLog : public Widget
{
public:
	TextLog(const char* label, int w, int h, int x=0, int y=0, int maxLines=256);
	~TextLog();
	WidgetType GetType() const {return TEXTLOG;}

	void Clear();
	void AddLine(const char* tag, const char* message, Pixel tagColor=0xFFFFFFFF);

	void SetPos(int x, int y);
	void SetSize(int w, int h);

	void Tick(float deltaTime);
	void Draw();

private:
	ScrollBar mScrollBar;
	TextLogDataImp* myData;
	int myMaxValueLines;
};

class CurveGraph : public Widget
{
public:
	struct CurvePoint
	{
		CurvePoint(unsigned int Id, float X, float Y) : id(Id), x(X), y(Y) {}
		unsigned int id;
		float x, y;
	};

	CurveGraph(const char* label, int w, int h, int x=0, int y=0, Vec2f startPoint=Vec2f(0,0), Vec2f endPoint=Vec2f(1,1), Spline2f::SplineType splineType=Spline2f::SPLINE_BEZIER_QUAD);

	WidgetType GetType() const {return CURVEGRAPH;}

	void Tick(float deltaTime);
	void Draw();

	void SetValue(Spline2f value);
	Spline2f& GetValue(){return mSpline;}

	float GetCurveValue(float valueIn){ return mSpline.GetPointAtTime(valueIn).y; }
private:
	bool InsideGraph(float x, float y);
	Vec2f PointCoord(int graphX, int graphY);
	Vec2i GraphCoord(float pointX, float pointY);

	Spline2f mSpline;
	Spline2f::SplineType mSplineType;
	std::vector<CurvePoint> myPoints;
	unsigned int myDragPointId, myMouseOverPointId;
	static unsigned int myIdPool;
};

}; // namespace tmpl